/*
 * Copyright (C) 2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

/*
 * Prototypes
 */
#if PROTO
static void
vc_and_c_vc(vc_t *u, c_t v, vc_t *w, eint32_t m);

static void
vc_and_vc_c(vc_t *u, vc_t *v, c_t w, eint32_t m);

static void
vc_and_vc_vc(vc_t *u, vc_t *v, vc_t *w, eint32_t m);

static void
vs_and_s_vs(vs_t *u, s_t v, vs_t *w, eint32_t m);

static void
vs_and_vs_s(vs_t *u, vs_t *v, s_t w, eint32_t m);

static void
vs_and_vs_vs(vs_t *u, vs_t *v, vs_t *w, eint32_t m);

static void
vi_and_i_vi(vi_t *u, i_t v, vi_t *w, eint32_t m);

static void
vi_and_vi_i(vi_t *u, vi_t *v, i_t w, eint32_t m);

static void
vi_and_vi_vi(vi_t *u, vi_t *v, vi_t *w, eint32_t m);

static void
vl_and_l_vl(vl_t *u, l_t v, vl_t *w, eint32_t m);

static void
vl_and_vl_l(vl_t *u, vl_t *v, l_t w, eint32_t m);

static void
vl_and_vl_vl(vl_t *u, vl_t *v, vl_t *w, eint32_t m);

static void
vo_and_i_vo(vo_t *u, l_t v, vo_t *w, eint32_t m);

static void
vo_and_vo_i(vo_t *u, vo_t *v, l_t w, eint32_t m);

static void
vo_and_z_vo(vo_t *u, empz_t v, vo_t *w, eint32_t m);

static void
vo_and_vo_z(vo_t *u, vo_t *v, empz_t w, eint32_t m);

static void
vo_and_vo_vo(vo_t *u, vo_t *v, vo_t *w, eint32_t m);

static void
o_and_o_o(o_t *u, o_t *v, o_t *w);
#endif

/*
 * Implementation
 */
#if CODE
void
evector_int_and(eint_t a, evector_t *b, etype_t t)
{
    eint32_t	 l;
    evector_t	*v;
    etype_t	 vt;
    v_enter();

    l = b->length;
    v_check(2);
    if (a > 2147483647 || a < -2147483647)
	vt = match(t_int64, t, 1, 0);
    else if (a > 32767 || a < -32767)
	vt = match(t_int32, t, 1, 0);
    else if (a > 127 || a < -127)
	vt = match(t_int16, t, 1, 0);
    else
	vt = match(t_int8, t, 1, 0);
    if (vt != t)
	b = coerce(b, t, vt);
    v = create(b, vt);
    switch (vt) {
	case t_int8:
	    vc_and_c_vc(v->v.obj, a, b->v.obj, vc_length(l));
	    break;
	case t_int16:
	    vs_and_s_vs(v->v.obj, a, b->v.obj, vs_length(l));
	    break;
	case t_int32:
	    vi_and_i_vi(v->v.obj, a, b->v.obj, vi_length(l));
	    break;
	case t_int64:
	    vl_and_l_vl(v->v.obj, a, b->v.obj, vl_length(l));
	    break;
	default:
	    vo_and_i_vo(v->v.obj, a, b->v.obj, l);
	    break;
    }
    v_leave();
}

void
evector_and_int(evector_t *a, eint_t b, etype_t t)
{
    eint32_t	 l;
    evector_t	*v;
    etype_t	 vt;
    v_enter();

    l = a->length;
    v_check(2);
    if (b > 2147483647 || b < -2147483647)
	vt = match(t_int64, t, 1, 0);
    else if (b > 32767 || b < -32767)
	vt = match(t_int32, t, 1, 0);
    else if (b > 127 || b < -127)
	vt = match(t_int16, t, 1, 0);
    else
	vt = match(t_int8, t, 1, 0);
    if (vt != t)
	a = coerce(a, t, vt);
    v = create(a, vt);
    switch (vt) {
	case t_int8:
	    vc_and_vc_c(v->v.obj, a->v.obj, b, vc_length(l));
	    break;
	case t_int16:
	    vs_and_vs_s(v->v.obj, a->v.obj, b, vs_length(l));
	    break;
	case t_int32:
	    vi_and_vi_i(v->v.obj, a->v.obj, b, vi_length(l));
	    break;
	case t_int64:
	    vl_and_vl_l(v->v.obj, a->v.obj, b, vl_length(l));
	    break;
	default:
	    vo_and_vo_i(v->v.obj, a->v.obj, b, l);
	    break;
    }
    v_leave();
}

void
evector_mpz_and(empz_t a, evector_t *b, etype_t t)
{
    evector_t	*v;
    v_enter();

    v_check(2);
    if (t != t_void)
	b = coerce(b, t, t_void);
    v = create(b, t_void);
    vo_and_z_vo(v->v.obj, a, b->v.obj, v->length);
    v_leave();
}

void
evector_and_mpz(evector_t *a, empz_t b, etype_t t)
{
    evector_t	*v;
    v_enter();

    v_check(2);
    if (t != t_void)
	a = coerce(a, t, t_void);
    v = create(a, t_void);
    vo_and_vo_z(v->v.obj, a->v.obj, b, v->length);
    v_leave();
}

void
evector_and(evector_t *a, evector_t *b, etype_t at, etype_t bt)
{
    eint32_t	l;
    evop_t	o;
    v_enter();

    setup(&o, a, b, at, bt, 1, 0);
    l = o.r->length;
    switch (o.t) {
	case t_int8:
	    vc_and_vc_vc(o.r->v.obj, o.a->v.obj, o.b->v.obj, vc_length(l));
	    break;
	case t_int16:
	    vs_and_vs_vs(o.r->v.obj, o.a->v.obj, o.b->v.obj, vs_length(l));
	    break;
	case t_int32:
	    vi_and_vi_vi(o.r->v.obj, o.a->v.obj, o.b->v.obj, vi_length(l));
	    break;
	case t_int64:
	    vl_and_vl_vl(o.r->v.obj, o.a->v.obj, o.b->v.obj, vl_length(l));
	    break;
	default:
	    vo_and_vo_vo(o.r->v.obj, o.a->v.obj, o.b->v.obj, l);
	    break;
    }
    v_leave();
}

static void
vc_and_c_vc(vc_t *u, c_t v, vc_t *w, eint32_t m)
{
#if HAVE_SIMD
    vc_t	vc = { v, v, v, v, v, v, v, v, v, v, v, v, v, v, v, v };
#else
    register c_t vc = v;
#endif

    while (m--)
	*u++ = vc & *w++;
}

static void
vc_and_vc_c(vc_t *u, vc_t *v, c_t w, eint32_t m)
{
#if HAVE_SIMD
    vc_t	wc = { w, w, w, w, w, w, w, w, w, w, w, w, w, w, w, w };
#else
    register c_t wc = w;
#endif

    while (m--)
	*u++ = *v++ & wc;
}

static void
vc_and_vc_vc(vc_t *u, vc_t *v, vc_t *w, eint32_t m)
{
    while (m--)
	*u++ = *v++ & *w++;
}

static void
vs_and_s_vs(vs_t *u, s_t v, vs_t *w, eint32_t m)
{
#if HAVE_SIMD
    vs_t	vs = { v, v, v, v, v, v, v, v };
#else
    register s_t vs = v;
#endif

    while (m--)
	*u++ = vs & *w++;
}

static void
vs_and_vs_s(vs_t *u, vs_t *v, s_t w, eint32_t m)
{
#if HAVE_SIMD
    vs_t	ws = { w, w, w, w, w, w, w, w };
#else
    register s_t ws = w;
#endif

    while (m--)
	*u++ = *v++ & ws;
}

static void
vs_and_vs_vs(vs_t *u, vs_t *v, vs_t *w, eint32_t m)
{
    while (m--)
	*u++ = *v++ & *w++;
}

static void
vi_and_i_vi(vi_t *u, i_t v, vi_t *w, eint32_t m)
{
#if HAVE_SIMD
    vi_t	vi = { v, v, v, v };
#else
    register i_t vi = v;
#endif

    while (m--)
	*u++ = vi & *w++;
}

static void
vi_and_vi_i(vi_t *u, vi_t *v, i_t w, eint32_t m)
{
#if HAVE_SIMD
    vi_t	wi = { w, w, w, w };
#else
    register i_t wi = w;
#endif

    while (m--)
	*u++ = *v++ & wi;
}

static void
vi_and_vi_vi(vi_t *u, vi_t *v, vi_t *w, eint32_t m)
{
    while (m--)
	*u++ = *v++ & *w++;
}

static void
vl_and_l_vl(vl_t *u, l_t v, vl_t *w, eint32_t m)
{
#if HAVE_SIMD
    vl_t	vl = { v, v };
#else
    register l_t vl = v;
#endif

    while (m--)
	*u++ = vl & *w++;
}

static void
vl_and_vl_l(vl_t *u, vl_t *v, l_t w, eint32_t m)
{
#if HAVE_SIMD
    vl_t	wl = { w, w };
#else
    register l_t wl = w;
#endif

    while (m--)
	*u++ = *v++ & wl;
}

static void
vl_and_vl_vl(vl_t *u, vl_t *v, vl_t *w, eint32_t m)
{
    while (m--)
	*u++ = *v++ & *w++;
}

static void
vo_and_i_vo(vo_t *u, l_t v, vo_t *w, eint32_t m)
{
    for (; m; u++, w++, m--) {
	switch (eobject_type(*w)) {
	    case t_void:
		set_i(0);
		break;
	    case t_int:
		set_i(v & get_i(w));
		break;
	    case t_mpz:
		if (ez_and_i_z(thr_zr, v, get_z(w)))
		    set_i(empz_get_i(thr_zr));
		else
		    set_z(thr_zr);
		break;
	    default:
		eraise_exception(except_not_an_integer);
	}
    }
}

static void
vo_and_vo_i(vo_t *u, vo_t *v, l_t w, eint32_t m)
{
    for (; m; u++, v++, m--) {
	switch (eobject_type(*v)) {
	    case t_void:
		set_i(0);
		break;
	    case t_int:
		set_i(get_i(v) & w);
		break;
	    case t_mpz:
		if (ez_and_z_i(thr_zr, get_z(v), w))
		    set_i(empz_get_i(thr_zr));
		else
		    set_z(thr_zr);
		break;
	    default:
		eraise_exception(except_not_an_integer);
	}
    }
}

static void
vo_and_z_vo(vo_t *u, empz_t v, vo_t *w, eint32_t m)
{
    ebool_t	b;

    if (unlikely((b = v == thr_zr))) {
	++thread_self->esp;
	eget_mpz(v, (thread_self->esp - thread_self->ess) - 1);
	v = thread_self->esp[-1].v.o;
    }
    for (; m; u++, w++, m--) {
	switch (eobject_type(*w)) {
	    case t_void:
		set_i(0);
		break;
	    case t_int:
		if (ez_and_z_i(thr_zr, v, get_i(w)))
		    set_i(empz_get_i(thr_zr));
		else
		    set_z(thr_zr);
		break;
	    case t_mpz:
		if (ez_and_z_z(thr_zr, v, get_z(w)))
		    set_i(empz_get_i(thr_zr));
		else
		    set_z(thr_zr);
		break;
	    default:
		eraise_exception(except_not_an_integer);
	}
    }
    if (b)
	--thread_self->esp;
}

static void
vo_and_vo_z(vo_t *u, vo_t *v, empz_t w, eint32_t m)
{
    ebool_t	b;

    if (unlikely((b = w == thr_zr))) {
	++thread_self->esp;
	eget_mpz(w, (thread_self->esp - thread_self->ess) - 1);
	w = thread_self->esp[-1].v.o;
    }
    for (; m; u++, v++, m--) {
	switch (eobject_type(*v)) {
	    case t_void:
		set_i(0);
		break;
	    case t_int:
		if (ez_and_i_z(thr_zr, get_i(v), w))
		    set_i(empz_get_i(thr_zr));
		else
		    set_z(thr_zr);
		break;
	    case t_mpz:
		if (ez_and_z_z(thr_zr, get_z(v), w))
		    set_i(empz_get_i(thr_zr));
		else
		    set_z(thr_zr);
		break;
	    default:
		eraise_exception(except_not_an_integer);
	}
    }
    if (b)
	--thread_self->esp;
}

static void
vo_and_vo_vo(vo_t *u, vo_t *v, vo_t *w, eint32_t m)
{
    for (; m; m--, u++, v++, w++)
	o_and_o_o(u, v, w);
}

static void
o_and_o_o(o_t *u, o_t *v, o_t *w)
{
    switch (eobject_type(*v)) {
	case t_void:
	    switch (eobject_type(*w)) {
		case t_void:	case t_int:	case t_mpz:
		    set_i(0);
		    break;
		default:
		    eraise_exception(except_not_an_integer);
	    }
	    break;
	case t_int:
	    switch (eobject_type(*w)) {
		case t_void:
		    set_i(0);
		    break;
		case t_int:
		    set_i(get_i(v) & get_i(w));
		    break;
		case t_mpz:
		    if (ez_and_i_z(thr_zr, get_i(v), get_z(w)))
			set_i(empz_get_i(thr_zr));
		    else
			set_z(thr_zr);
		    break;
		default:
		    eraise_exception(except_not_an_integer);
	    }
	    break;
	case t_mpz:
	    switch (eobject_type(*w)) {
		case t_void:
		    set_i(0);
		    break;
		case t_int:
		    if (ez_and_z_i(thr_zr, get_z(v), get_i(w)))
			set_i(empz_get_i(thr_zr));
		    else
			set_z(thr_zr);
		    break;
		case t_mpz:
		    if (ez_and_z_z(thr_zr, get_z(v), get_z(w)))
			set_i(empz_get_i(thr_zr));
		    else
			set_z(thr_zr);
		    break;
		default:
		    eraise_exception(except_not_an_integer);
	    }
	    break;
	default:
	    eraise_exception(except_not_an_integer);
    }
}
#endif
